#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
/*
 * Copyright © 2014, Finium Sdn Bhd, All Rights Reserved
 * 
 * HibernateCriteriaQueryUtil.java
 * Modification History
 * *************************************************************
 * Date				Author						Comment
 * Oct 26, 2013		Venkaiah Chowdary Koneru	Created
 * *************************************************************
 */
package ${package}.support.hibernate;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import ${package}.dto.ListProp;
import ${package}.support.CommonsUtil;

import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.springframework.util.StringUtils;

/**
 * Utility class to hold Hibernate Criteria Queries functionality.
 * 
 * @author Venkaiah Chowdary Koneru
 */
public final class HibernateCriteriaQueryUtil extends AbstractHibernateUtil
{
    /**
     * Helper method to ease the query process.
     * 
     * @param criteria
     *            criteria alias names.
     * @param entityClass
     *            the base entity class
     * @param aliasProjectionPropertyMap
     *            alias and the associated property name map for criteria
     *            projection section and also Sorting purposes.
     * @param restrictionsCriteria
     *            restrictions. can be null.
     * @param listProp
     *            can be null.
     * @param resultClass
     *            can be null.
     * @return List of the result object. Total records count will be returned
     *         in the queryProp.pagination.totalRecords property.
     */
    public static List<?> list(final Criteria criteria,
            final Class<?> entityClass,
            Map<String, String> aliasProjectionPropertyMap,
            final List<Criterion> restrictionsCriteria,
            final ListProp listProp, final Class<?> resultClass)
    {
        if (criteria == null)
        {
            return null;
        }

        if (!CommonsUtil.isEmpty(restrictionsCriteria))
        {
            for (Criterion restriction : restrictionsCriteria)
            {
                criteria.add(restriction);
            }
        }

        // Set Total Records to list prop
        setCountData(criteria, listProp);

        // Update sorting order to criteria
        generateSorting(criteria, listProp, aliasProjectionPropertyMap);

        // Add projections to criteria
        setProjections(criteria, aliasProjectionPropertyMap);

        criteria.setResultTransformer(new AliasToBeanNestedResultTransformer(
                resultClass));

        // Update pagination data
        setPagination(listProp, criteria);

        return criteria.list();
    }

    /**
     * Adds the sorting order to criteria.
     * 
     * @param criteria
     *            criteria object. cannot be null.
     * @param listProp
     *            list prop object
     * @param aliasAndPropertyMap
     *            alias and the associated property name map. This field is
     *            optional. if it is null or not containing the property for the
     *            sorting, then the sorting.property value will be used.
     */
    private static void generateSorting(Criteria criteria, ListProp listProp,
            Map<String, String> aliasAndPropertyMap)
    {
        if (criteria == null || listProp == null
                || !StringUtils.hasText(listProp.getSortProperty()))
        {
            return;
        }

        String sortProperty = listProp.getSortProperty();

        if (aliasAndPropertyMap != null
                && aliasAndPropertyMap.containsKey(sortProperty))
        {
            sortProperty = aliasAndPropertyMap.get(sortProperty);
        }

        if (listProp.isSortAscending())
        {
            criteria.addOrder(Order.asc(sortProperty));
        }
        else
        {
            criteria.addOrder(Order.desc(sortProperty));
        }
    }

    /**
     * to set the projections from alias projections map to criteria.
     * 
     * @param criteria
     * @param aliasProjectionPropertyMap
     */
    private static void setProjections(Criteria criteria,
            Map<String, String> aliasProjectionPropertyMap)
    {
        if (!CommonsUtil.isEmpty(aliasProjectionPropertyMap))
        {
            ProjectionList projectionList = Projections.projectionList();
            for (Entry<String, String> projectionPropertyAlias : aliasProjectionPropertyMap
                    .entrySet())
            {
                projectionList.add(Projections.property(
                        projectionPropertyAlias.getValue()).as(
                        projectionPropertyAlias.getKey()));
            }
            criteria.setProjection(projectionList);
        }
    }

    /**
     * Set pagination values into the Criteria object.
     * 
     * @param listProp
     *            list prop object
     * @param criteria
     *            pagination values will be set into this criteria object. must
     *            not be null.
     */
    private static void setPagination(ListProp listProp, Criteria criteria)
    {
        if (listProp == null || criteria == null
                || !(listProp.getStartIndex() >= getStartIndex()))
        {
            return;
        }

        if (updatePagination(listProp))
        {
            criteria.setFirstResult((int) listProp.getStartIndex());
            criteria.setMaxResults((int) (listProp.getEndIndex()
                    - listProp.getStartIndex() + 1));
        }
    }

    /**
     * retirieves total records number and assign to list prop
     * 
     * @param criteria
     * @param listProp
     */
    private static void setCountData(Criteria criteria, ListProp listProp)
    {
        if (criteria == null || listProp == null)
        {
            return;
        }

        if (listProp.getStartIndex() > getStartIndex())
        {
            criteria.setProjection(Projections.rowCount());

            List<?> countList = criteria.list();
            if (CommonsUtil.isEmpty(countList))
            {
                listProp.setTotalRecords(0);
            }
            else
            {
                listProp.setTotalRecords(((Long) countList.iterator().next())
                        .longValue());
            }
        }
    }

    /**
     * Constructor. private to prevent unnecessary instantiation.
     */
    private HibernateCriteriaQueryUtil()
    {
    }
}
